/* glcd.c

Copyright (C) Alex S. (mailto: md5sum@alexsp.ru)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details
http://www.gnu.org/licenses/gpl-2.0.html.

You should have received a copy of the GNU General Public License
along with this program in the file gpl-2.0.txt;
if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "glcd_boot.h"
#include "fonts.h"

#define nop()  __asm__ __volatile__("nop\r\nnop\r\nnop\r\nnop")
#define GLCD_A	GLCD_contr &=~GLCD_CSA;GLCD_contr|=GLCD_CSB
#define GLCD_B	GLCD_contr &=~GLCD_CSB;GLCD_contr|=GLCD_CSA

#define START_TIME 87 /*Начальная позиция вывода часов*/
#define TIME_OFFSET 5 /*Смещение часов вниз - не более 5!*/
#define START_DELTA_P 47 /*Позиция отклонения давления*/
#define START_TEMP 0
#define TEMP_OFFSET 2
#define DELTA_T_OFFSET 3

static uint8_t lcd_x=0,lcd_y=0;
static void GLCD_put_command(const uint8_t data);
void GLCD_xy(const uint8_t x, const uint8_t y);

inline static uint8_t GLCD_get_status(){
	uint8_t tmp;
	GLCD_read();
	GLCD_contr&=~GLCD_RS;
	GLCD_DDRx=0;
	GLCD_PORTx=0xff;
	GLCD_contr|=GLCD_E;
	nop();
	tmp=GLCD_PINx;
	GLCD_contr&=~GLCD_E;
	return tmp&GLCD_busy;
}

static void GLCD_select_cs() {
	if (lcd_x==0x40) {
		GLCD_B;
		GLCD_put_command(0x40);
		GLCD_put_command(0xb8|(lcd_y&0x07));
	}
	if (lcd_x==0x80) {
		lcd_x=0;
		if (lcd_y<7) lcd_y++; else lcd_y=0;
	}
	if (lcd_x==0) {
		GLCD_A;
		GLCD_put_command(0x40);
		GLCD_put_command(0xb8|(lcd_y&0x07));
	}

}

uint8_t GLCD_get_data(){
	uint8_t tmp;

	//	while (GLCD_get_status());
	GLCD_read();
	GLCD_contr|=GLCD_RS;
	GLCD_DDRx=0;
	GLCD_PORTx=0xff;
	GLCD_contr|=GLCD_E;
	nop();
	tmp=GLCD_PINx;
	GLCD_contr&=~GLCD_E;
	lcd_x++;
	GLCD_select_cs();
	return tmp;
}


void GLCD_put_data(const uint8_t data){
	while (GLCD_get_status());
	GLCD_write();
	GLCD_contr|=GLCD_RS;
	GLCD_PORTx = data;
	GLCD_DDRx=0xff;
	nop();
	GLCD_contr|=GLCD_E;
	nop();
	GLCD_contr&=~GLCD_E;
	lcd_x++;
	GLCD_select_cs();
}

static void GLCD_put_command(const uint8_t data){
	while (GLCD_get_status());
	GLCD_write();
	GLCD_contr&=~GLCD_RS;
	GLCD_PORTx = data;
	GLCD_DDRx=0xff;
	nop();
	GLCD_contr|=GLCD_E;
	nop();
	GLCD_contr&=~GLCD_E;
}

void GLCD_clear() {
	uint16_t i;
	GLCD_xy(0,0);
	for (i=0;i<128*8;i++) {
		GLCD_put_data(0x00);
	}
}

void GLCD_init() {
	GLCD_contr&=~GLCD_RST;
	GLCD_contr&=~GLCD_E;
	GLCD_contr_ddr|=(GLCD_CSA|GLCD_CSB|GLCD_RS|GLCD_RW|GLCD_E|GLCD_RST);
	GLCD_PORTx=0;
	GLCD_DDRx=0x0;
	GLCD_contr|=GLCD_CSA|GLCD_CSB|GLCD_RW;
	GLCD_contr|=GLCD_RST;
	nop();
	GLCD_B;
	GLCD_put_command(0xc0);
	GLCD_put_command(0x3f);
	GLCD_A;
	GLCD_put_command(0xc0);
	GLCD_put_command(0x3f);
	GLCD_clear();
	GLCD_xy(0,0);
}

/*from left(0),top(0) to right(127),bottom(7)*/
void GLCD_xy(const uint8_t x, const uint8_t y) {
	lcd_y=y;
	lcd_x=x;
	if (lcd_x<0x40) {GLCD_A;} else {GLCD_B;}
	GLCD_put_command(0x40|(lcd_x&0x3f));
	GLCD_put_command(0xb8|(lcd_y&0x07));
}

void GLCD_print_mem(const prog_uint8_t *c,const uint8_t len, const uint8_t offset, const uint8_t mask){
	uint8_t tmp,x,y;
	uint16_t a;
	uint8_t i;
	prog_uint8_t *p;
	p=(prog_uint8_t *)c;
	for (i=0;i<len;i++) {
		tmp=pgm_read_byte(p++);
		x=lcd_x;
		y=lcd_y;
		GLCD_xy(x,y);
		GLCD_get_data();
		GLCD_xy(x,y);
		a=(uint16_t)GLCD_get_data();
		if (y<7) {
			GLCD_xy(x,y+1);
			GLCD_get_data();
			GLCD_xy(x,y+1);
			a|=(((uint16_t)GLCD_get_data())<<8);
		}
		a&=~(((uint16_t)mask)<<offset);
		a|=(((uint16_t)tmp&mask)<<offset);
		GLCD_xy(x,y);
		GLCD_put_data((uint8_t)(a&0xff));
		if (y<7) {
			GLCD_xy(x,y+1);
			GLCD_put_data((uint8_t)((a>>8)&0xff));
		}
		GLCD_xy(x+1,y);
	}
}


void GLCD_print_5x8(const uint8_t c, const uint8_t offset, uint8_t space) {
	GLCD_print_mem((const prog_uint8_t *)&font_5x8[c][0],5,offset,0xff);
	if (space) GLCD_print_mem((const prog_uint8_t *)font_5x8_sp,1,offset,0xff);
}
/*
void GLCD_print_3x5(const uint8_t c, const uint8_t offset, uint8_t space) {
	GLCD_print_mem((const prog_uint8_t *)&font_3x5[c][0],3,offset,0x1f);
	if (space) GLCD_print_mem((const prog_uint8_t *)font_5x8_sp,1,offset,0x1f);
}

void GLCD_print_10x16(const uint8_t c, const uint8_t offset, uint8_t space) {
	uint8_t x=lcd_x;
	uint8_t y=lcd_y;
	GLCD_print_mem((const prog_uint8_t *)&font_10x16[c][0][0],10,offset,0xff);
	if (space) GLCD_print_mem((const prog_uint8_t *)font_5x8_sp,1,offset,0xff);
	GLCD_xy(x,y+1);
	GLCD_print_mem((const prog_uint8_t *)&font_10x16[c][1][0],10,offset,0x7f);
	if (space) GLCD_print_mem((const prog_uint8_t *)font_5x8_sp,1,offset,0x7f);
	if (space) GLCD_xy(x+11,y); else GLCD_xy(x+10,y);
}

void GLCD_print_12x16(const uint8_t c, const uint8_t offset, uint8_t space) {
	uint8_t x=lcd_x;
	uint8_t y=lcd_y;
	GLCD_print_mem((const prog_uint8_t *)&font_12x16[c][0][0],12,offset,0xff);
	//	if (space) GLCD_print_mem((const prog_uint8_t *)font_5x8_sp,1,offset,0xff);
	GLCD_xy(x,y+1);
	GLCD_print_mem((const prog_uint8_t *)&font_12x16[c][1][0],12,offset,0x7f);
	//	if (space) GLCD_print_mem((const prog_uint8_t *)font_5x8_sp,1,offset,0x7f);
	if (space) GLCD_xy(x+12,y); else GLCD_xy(x+11,y);
}
*/

static uint8_t n10(const uint16_t a){
	uint16_t tmp;
	tmp=a;
	tmp%=100;
	return tmp/10;
}
static uint8_t n1(const uint16_t a){
	uint16_t tmp;
	tmp=a;
	tmp%=10;
	return tmp;
}

void GLCD_print_logo(const uint8_t offset,uint16_t baud) {
	uint8_t x=lcd_x;
	uint8_t y=lcd_y;
	GLCD_print_mem((const prog_uint8_t *)&FW_upgr_128x24[0][0],128,offset,0xff);
	GLCD_xy(x,y+1);
	GLCD_print_mem((const prog_uint8_t *)&FW_upgr_128x24[1][0],128,offset,0xff);
	GLCD_xy(x,y+2);
	GLCD_print_mem((const prog_uint8_t *)&FW_upgr_128x24[2][0],128,offset,0xff);
	GLCD_xy(x+24,y+3);
	GLCD_print_mem((const prog_uint8_t *)&prot_stk500[0],80,offset+2,0xff);
	GLCD_xy(x+27,y+4);
	GLCD_print_mem((const prog_uint8_t *)&set_serial_a[0],27,offset+3,0xff);
	if (baud==38400) {GLCD_print_mem((const prog_uint8_t *)&set_serial_s[0][0],14,offset+3,0xff);}
	else if (baud==19200) {GLCD_print_mem((const prog_uint8_t *)&set_serial_s[1][0],14,offset+3,0xff);}
	else {GLCD_print_mem((const prog_uint8_t *)&set_serial_s[2][0],14,offset+3,0xff);}
	GLCD_print_mem((const prog_uint8_t *)&set_serial_b[0],32,offset+3,0xff);

}

void print_load_addr(uint16_t a) {
	GLCD_print_mem((const prog_uint8_t *)&load_prog[0],19,0,0xff);
	GLCD_print_mem((const prog_uint8_t *)&font_5x8_sp[0],3,0,0xff);
	if (a>9) GLCD_print_5x8(n10(a),0,0);
	GLCD_print_5x8(n1(a),0,0);
	GLCD_print_mem((const prog_uint8_t *)&font_5x8_sp[0],3,0,0xff);
	GLCD_print_mem((const prog_uint8_t *)&KiB[0],9,0,0xff);

//	while (lcd_x<read_x) GLCD_print_mem((const prog_uint8_t *)&font_5x8_sp[0],1,0,0xff);
}

void print_read_addr(uint16_t a) {
	GLCD_print_mem((const prog_uint8_t *)&verified[0],34,0,0xff);
	GLCD_print_mem((const prog_uint8_t *)&font_5x8_sp[0],3,0,0xff);
	if (a>9) GLCD_print_5x8(n10(a),0,0);
	GLCD_print_5x8(n1(a),0,0);
	GLCD_print_mem((const prog_uint8_t *)&font_5x8_sp[0],3,0,0xff);
	GLCD_print_mem((const prog_uint8_t *)&KiB[0],9,0,0xff);

//	while (lcd_x<127) GLCD_print_mem((const prog_uint8_t *)&font_5x8_sp[0],1,0,0xff);
}
